package io.kblog.service.impl

import io.kblog.dao.BaseDao
import io.kblog.domain.Base
import io.kblog.service.BaseService
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Lazy
import org.springframework.data.domain.*
import org.springframework.data.jpa.domain.Specification
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.RequestBody
import java.rmi.NoSuchObjectException

abstract class BaseServiceImpl<T, ID> : BaseService<T, ID> {
    @Lazy
    @Autowired
    override lateinit var baseDao: BaseDao<T, ID>

    protected val logger: Logger
        get() = LoggerFactory.getLogger(this.javaClass.declaringClass)

    override fun findAll(): List<T> = baseDao.findAll()

    override fun findAll(spec: Specification<T>, pageable: Pageable): Page<T> = baseDao.findAll(spec, pageable)

    override fun findAll(spec: Specification<T>, page: Int, size: Int): Page<T> =
        if (size == 0) PageImpl(emptyList(), PageRequest.ofSize(1), count(spec)) else baseDao.findAll(
            spec,
            PageRequest.of(page, size)
        )

    override fun findAll(spec: Specification<T>): List<T> = baseDao.findAll(spec)

    override fun findAll(pageable: Pageable): Page<T> = baseDao.findAll(pageable)

    override fun findAll(page: Int, size: Int): Page<T> =
        if (size == 0) PageImpl(emptyList(), PageRequest.ofSize(1), count()) else baseDao.findAll(
            PageRequest.of(
                page,
                size
            )
        )

    override fun findAllByExample(bean: T & Any): List<T> = baseDao.findAll(Example.of(bean))

    override fun findAllByExample(example: Example<T>, pageable: Pageable): Page<T> = baseDao.findAll(example, pageable)

    override fun findAllByExample(example: Example<T>, page: Int?, size: Int?): Page<T> =
        if (size == 0) PageImpl(emptyList(), PageRequest.ofSize(1), count(example)) else findAllByExample(
            example,
            PageRequest.of(page ?: 0, size ?: Int.MAX_VALUE, Sort.by(Sort.Direction.DESC, "id"))
        )

    override fun findAllByExample(bean: T & Any, pageable: Pageable): Page<T> =
        baseDao.findAll(Example.of(bean), pageable)

    override fun findAllByExample(bean: T & Any, page: Int?, size: Int?): Page<T> =
        if (size == 0) PageImpl(emptyList(), PageRequest.ofSize(1), count(bean)) else findAllByExample(
            Example.of(bean),
            PageRequest.of(page ?: 0, size ?: Int.MAX_VALUE, Sort.by(Sort.Direction.DESC, "id"))
        )

    override fun findAllById(ids: List<ID>): List<T> = baseDao.findAllById(ids)

    override fun get(id: ID & Any): T? = baseDao.findById(id).orElse(null)

    override fun getByExample(bean: T & Any): T? = baseDao.findOne(Example.of(bean)).orElse(null)

    override fun get(spec: Specification<T>): T? = baseDao.findOne(spec).orElse(null)

    override fun count() = baseDao.count()

    override fun count(bean: T & Any) = baseDao.count(Example.of(bean))

    override fun count(example: Example<T>) = baseDao.count(example)

    override fun count(spec: Specification<T>) = baseDao.count(spec)

    override fun save(bean: T & Any): T = baseDao.save(bean)

    override fun saveAll(beans: List<T>): List<T> = baseDao.saveAll(beans)

    override fun saveByVo(@RequestBody vo: T): T {
        return if ((vo as Base<ID>).id == null) save(vo)
        else updateByVo(vo)
    }

    override fun update(entity: T): T = save(entity!!)

    override fun delete(entity: T) = baseDao.delete(entity!!)

    override fun deleteByExample(bean: T) = baseDao.deleteAll(findAllByExample(bean!!))

    override fun deleteById(id: ID & Any) = baseDao.deleteById(id)

    @Transactional
    override fun deleteByIdIn(ids: List<ID>) = baseDao.deleteByIdIn(ids)

    override fun deleteAll(entityList: List<T>) = baseDao.deleteAll(entityList)

    override fun deleteAllInBatch(entityList: List<T>) = baseDao.deleteAllInBatch(entityList)

    override fun updateByVo(@RequestBody vo: T): T {
        return update(
            (get((vo as Base<ID>).id ?: throw NoSuchObjectException("VO ID ${vo.id} Not Found"))
                ?: throw NoSuchObjectException("VO ID ${vo.id} Not Found")).let { bean ->
                BeanUtils.copyProperties(vo as Any, bean as Any).run {
                    bean
                }
            })
    }
}
